package com.util_code.utils.javaio;

import com.util_code.utils.javajoda.StringPool;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;

/**
 * Consumes a stream.
 * For any <code>Process</code>, the input and error streams must read even
 * if the data written to these streams is not used by the application.
 * The generally accepted solution for this problem is a stream gobbler thread
 * that does nothing but consume data from an input stream until stopped.
 */
public class StreamGobbler extends Thread {

    protected final InputStream is;
    protected final String prefix;
    protected final OutputStream out;
    protected final Object lock = new Object();
    protected boolean end = false;

    public StreamGobbler(InputStream is) {
        this(is, null, null);
    }

    public StreamGobbler(InputStream is, OutputStream output) {
        this(is, output, null);
    }

    public StreamGobbler(InputStream is, OutputStream output, String prefix) {
        this.is = is;
        this.prefix = prefix;
        this.out = output;
    }

    @Override
    public void run() {
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);

        try {
            String line;
            while ((line = br.readLine()) != null) {
                if (out != null) {
                    if (prefix != null) {
                        out.write(prefix.getBytes());
                    }
                    out.write(line.getBytes());
                    out.write(StringPool.BYTES_NEW_LINE);
                }
            }
        } catch (IOException ioe) {
            if (out != null) {
                ioe.printStackTrace(new PrintStream(out));
            }
        } finally {
            if (out != null) {
                try {
                    out.flush();
                } catch (IOException ignore) {
                }
            }
            try {
                br.close();
            } catch (IOException ignore) {
            }
        }

        synchronized (lock) {
            lock.notifyAll();
            end = true;
        }
    }

    /**
     * Waits for gobbler to end.
     */
    public void waitFor() {
        try {
            synchronized (lock) {
                if (!end) {
                    lock.wait();
                }
            }
        } catch (InterruptedException ignore) {
            Thread.currentThread().interrupt();
        }
    }

}